#ifndef __THREADQUEUE__
#define __THREADQUEUE__

#include <queue>
#include <memory>
#include <mutex>
#include <condition_variable>

using std::queue;
using std::deque;
using std::mutex;
using std::condition_variable;


template<typename T>
class ThreadSafeQueue
{
private:
	mutable mutex mut;
	condition_variable dataCond;
	queue<T> dataQueue;
public:
	
	ThreadSafeQueue();
	ThreadSafeQueue(const ThreadSafeQueue& other)
	{
		std::lock_guard<mutex> lk(other.mut);
		dataQueue = other.dataQueue;
	}

	//a queue cannot be copied
	ThreadSafeQueue& operator = (const ThreadSafeQueue) = delete;
	
	bool TryPop(T& value)
	{
		std::lock_guard<mutex> lk(mut);
		if (!dataQueue.empty())
		{
			return false;
		}
		value = dataQueue.front();
		dataQueue.pop();
		return true;
	}
	std::shared_ptr<T> TryPop()
	{
		std::lock_guard<mutex> lk(mut);
		if (!dataQueue.empty())
		{
			return std::shared_ptr<T>();
		}
		std::shared_ptr<T> res(std::make_shared<T>(dataQueue.front()));
		dataQueue.pop();
		return res;
	}
	
	bool Push(T& value)
	{
		try
		{
			std::lock_guard<mutex> lk(mut);
			dataQueue.push(value);

			#ifdef NOTIFY_ALL
				dataCond.notify_all();
			#endif
		}
		catch (std::exception e)
		{
			return false;
		}
	}

	bool empty()
	{
		std::lock_guard<mutex> lk(mut);
		return dataQueue.empty();
	}

	int GetSize()
	{
		return dataQueue.size();
	}

	void push(T newValue)
	{
		std::lock_guard<mutex> lk(mut);
		dataQueue.push(newValue);
	}


};

#endif // !__THREADQUEUE__
